﻿using System;
using System.Collections.ObjectModel;
using System.Linq;

namespace Creventive.Wpf.ShellFactory.Workspace.Primitives
{
    /// <summary>
    ///   A collection of items which are logically connected to a parent object
    /// </summary>
    /// <typeparam name = "T"></typeparam>
    public class LogicalChildCollection<T> : ObservableCollection<T>
    {
        private readonly Action<int, T> addedCallback;
        private readonly Action<int, T> removedCallback;

        /// <summary>
        ///   Initializes a new instance of the <see cref = "LogicalChildCollection&lt;T&gt;" /> class.
        /// </summary>
        /// <param name = "addedCallback">The added callback.</param>
        /// <param name = "removedCallback">The removed callback.</param>
        public LogicalChildCollection(Action<int, T> addedCallback, Action<int, T> removedCallback)
        {
            if (addedCallback == null)
                throw new ArgumentNullException("addedCallback");
            if (removedCallback == null)
                throw new ArgumentNullException("removedCallback");
            this.addedCallback = addedCallback;
            this.removedCallback = removedCallback;
        }

        /// <summary>
        ///   Clears the items.
        /// </summary>
        protected override void ClearItems()
        {
            for (int i = this.Count; i >= 0; i--)
                this.RemoveAt(i);
        }

        /// <summary>
        ///   Inserts the item.
        /// </summary>
        /// <param name = "index">The index.</param>
        /// <param name = "newItem">The new item.</param>
        protected override void InsertItem(int index, T newItem)
        {
            base.InsertItem(index, newItem);
            this.Attach(index, newItem);
        }

        /// <summary>
        ///   Attaches the given item at the specified index.
        /// </summary>
        /// <param name = "index">The index.</param>
        /// <param name = "newItem">The new item.</param>
        protected void Attach(int index, T newItem)
        {
            this.addedCallback.Invoke(index, newItem);
        }

        /// <summary>
        ///   Removes the item.
        /// </summary>
        /// <param name = "index">The index.</param>
        protected override void RemoveItem(int index)
        {
            var oldItem = this[index];
            base.RemoveItem(index);
            this.Detach(index, oldItem);
        }

        /// <summary>
        ///   Sets the item.
        /// </summary>
        /// <param name = "index">The index.</param>
        /// <param name = "newItem">The new item.</param>
        protected override void SetItem(int index, T newItem)
        {
            var oldItem = this[index];
            base.SetItem(index, newItem);
            this.Detach(index, oldItem);
            this.Attach(index, newItem);
        }

        /// <summary>
        ///   Detaches the given item from the specified index.
        /// </summary>
        /// <param name = "index">The index.</param>
        /// <param name = "oldItem">The old item.</param>
        protected void Detach(int index, T oldItem)
        {
            this.removedCallback.Invoke(index, oldItem);
        }
    }
}